#include <tsin/config/XMLConfig.hpp>
#include <tsin/util/Logger.hpp>

#include <libxml/parser.h>
#include <libxml/tree.h>

#include <cstring>

#include <tsin/util/MemCheck.hpp>
using namespace tsin;

const char XMLConfig::CONFIG_FILE[] = "config.xml";
const char XMLConfig::CONFIG_HEADER[] = "config";
const char XMLConfig::CONFIG_OPTION[] = "option";

XMLConfig::XMLConfig()
        : Config()
{
        doc = xmlReadFile(CONFIG_FILE, NULL, 0);
        if (doc == NULL)
                return;

        parseXML(doc);
}

XMLConfig::~XMLConfig()
{
        if (file)
                delete[] file;

        if (doc != NULL)
                xmlFreeDoc(doc);
        xmlCleanupParser();
}


int XMLConfig::writeConfig()
{
        return 0;
}

int XMLConfig::parseXML(xmlDoc *doc)
{
        xmlNode *root = xmlDocGetRootElement(doc);

        if (strcmp((char *)root->name, CONFIG_HEADER) != 0)
                return -1;

        xmlNode *cur_node;
        for (cur_node = root->children; cur_node; cur_node = cur_node->next) {
                switch (cur_node->type) {
                case XML_ELEMENT_NODE:
                        if (strcmp((const char *)cur_node->name, CONFIG_OPTION) == 0)
                                checkOption((char*)xmlGetProp(cur_node, (const xmlChar *)"name"), 
                                            (char*)xmlGetProp(cur_node, (const xmlChar *)"value"));
                        break;
                default:
                        break;
                }
        }

        return 0;
}

int XMLConfig::checkOption(char *name, char *value)
{
#define TSIN_DEF_OPTION(optionName,elementName,type) \
        if (strcmp(name, #optionName) == 0) { \
                updateOption(value, &elementName, type); \
                return 0; \
        }

#include "options.def"

#undef TSIN_DEF_OPTION

        return -1;
}

int XMLConfig::updateOption(char *value, void *ptr, OptionType type)
{
        int tmp;

        switch(type)
        {
        case INT:
                *(int*)ptr = atoi(value);
                break;
        case STRING:
                tmp = strlen(value);
                *(char**)ptr = new char[tmp + 1];
                memcpy(*(char**)ptr, value, tmp);
                (*(char**)ptr)[tmp] = '\0';
                break;
        case BOOL:
                if (strcmp(value, "true") == 0)
                        *(bool*)ptr = true;
                else
                        *(bool*)ptr = false;
                break;
        default:
                return -1;
        }

        return 0;
}
